package com.peng.mybatis.builder.xml;

import cn.hutool.core.util.StrUtil;
import com.peng.mybatis.builder.BaseBuilder;
import com.peng.mybatis.datasource.DataSourceFactory;
import com.peng.mybatis.io.Resources;
import com.peng.mybatis.mapping.BoundSql;
import com.peng.mybatis.mapping.Environment;
import com.peng.mybatis.mapping.MappedStatement;
import com.peng.mybatis.mapping.SqlCommandType;
import com.peng.mybatis.session.Configuration;
import com.peng.mybatis.transaction.TransactionFactory;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import javax.sql.DataSource;
import java.io.Reader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
public class XmlConfigBuilder extends BaseBuilder {

    private Element root;

    public XmlConfigBuilder(Reader reader) {
        // 1. 调用父类初始化Configuration
        super(new Configuration());
        // 2. dom4j 处理 xml
        SAXReader saxReader = new SAXReader();
        try {
            Document doc = saxReader.read(new InputSource(reader));
            root = doc.getRootElement();
        } catch (DocumentException e) {
            log.error("解析XML配置出现异常：{}", e.getMessage(), e);
        }
    }

    public Configuration parse() {
        try {
            // 解析环境
            environmentsElement(root.element("environments"));

            // 解析映射器
            mapperElement(root.element("mappers"));
        } catch (Exception e) {
            throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
        return configuration;
    }

    private void environmentsElement(Element environments) throws Exception {
        String defaultDatasource = environments.attributeValue("default");
        List<Element> environmentList = environments.elements("environment");
        for (Element environment : environmentList) {
            // 数据源名称
            String id = environment.attributeValue("id");

            if (defaultDatasource.equals(id)) {
                // 事务管理器
                TransactionFactory transactionFactory = (TransactionFactory) typeAliasRegistry
                        .resolveAlias(environment.element("transactionManager").attributeValue("type"))
                        .newInstance();

                // 数据源
                Element dataSourceElement = environment.element("dataSource");
                DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry
                        .resolveAlias(dataSourceElement.attributeValue("type")).newInstance();
                List<Element> props = dataSourceElement.elements("property");
                Properties properties = props.stream().collect(
                        Collectors.toMap(
                                ele -> ele.attributeValue("name"),
                                ele -> ele.attributeValue("value"),
                                (a, b) -> a,
                                Properties::new
                        )
                );
                dataSourceFactory.setProperties(properties);
                DataSource dataSource = dataSourceFactory.getDataSource();

                configuration.setEnvironment(
                        Environment.builder()
                                .id(id)
                                .transactionFactory(transactionFactory)
                                .dataSource(dataSource)
                                .build()
                );
                break;
            }
        }
    }

    private void mapperElement(Element mappers) throws Exception {
        for (Element mapper : mappers.elements("mapper")) {
            String resource = mapper.attributeValue("resource");
            Reader reader = Resources.getResourceAsReader(resource);

            // 读取xxx_mapper.xml
            SAXReader saxReader = new SAXReader();
            Document mapperDoc = saxReader.read(new InputSource(reader));
            Element mapperRoot = mapperDoc.getRootElement();
            String namespace = mapperRoot.attributeValue("namespace");

            // 读取select配置项
            List<Element> selects = mapperRoot.elements("select");
            for (Element select : selects) {
                String id = select.attributeValue("id");
                String parameterType = select.attributeValue("parameterType");
                String resultType = select.attributeValue("resultType");
                String sql = select.getText();

                // 匹配EL表达式，将动态参数变成？
                Map<Integer, String> parameter = new HashMap<>();
                Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                Matcher matcher = pattern.matcher(sql);
                for (int i = 1; matcher.find(); i++) {
                    String g1 = matcher.group(1);
                    String g2 = matcher.group(2);
                    parameter.put(i, g2);
                    sql = sql.replace(g1, "?");
                }

                String msId = StrUtil.join(".", namespace, id);
                String name = select.getName();
                SqlCommandType sqlCommandType = SqlCommandType.valueOf(name.toUpperCase());
                configuration.addMappedStatement(MappedStatement.builder()
                        .id(msId)
                        .configuration(configuration)
                        .sqlCommandType(sqlCommandType)
                        .boundSql(BoundSql.builder()
                                .sql(sql)
                                .resultType(resultType)
                                .parameterType(parameterType)
                                .parameter(parameter)
                                .build())
                        .build());
            }
            configuration.addMapper(Resources.classForName(namespace));
        }
    }

}
